{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "Code_Simulation.ipynb",
      "provenance": [],
      "collapsed_sections": []
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.5.6"
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "accelerator": "GPU"
  },
  "cells": [
    {
      "cell_type": "code",
      "metadata": {
        "id": "L4MOKKo93AHB",
        "colab_type": "code",
        "outputId": "3b5b50a3-4e29-4a13-9636-7dd768595c54",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 306
        }
      },
      "source": [
        "# !nvidia-smi"
      ],
      "execution_count": 1,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Tue May 19 20:16:52 2020       \n",
            "+-----------------------------------------------------------------------------+\n",
            "| NVIDIA-SMI 440.82       Driver Version: 418.67       CUDA Version: 10.1     |\n",
            "|-------------------------------+----------------------+----------------------+\n",
            "| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |\n",
            "| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |\n",
            "|===============================+======================+======================|\n",
            "|   0  Tesla P100-PCIE...  Off  | 00000000:00:04.0 Off |                    0 |\n",
            "| N/A   37C    P0    27W / 250W |      0MiB / 16280MiB |      0%      Default |\n",
            "+-------------------------------+----------------------+----------------------+\n",
            "                                                                               \n",
            "+-----------------------------------------------------------------------------+\n",
            "| Processes:                                                       GPU Memory |\n",
            "|  GPU       PID   Type   Process name                             Usage      |\n",
            "|=============================================================================|\n",
            "|  No running processes found                                                 |\n",
            "+-----------------------------------------------------------------------------+\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "4seCuT_n89iY",
        "colab_type": "code",
        "outputId": "69fed152-1b4a-4b51-8f8a-94c8680b23bb",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 51
        }
      },
      "source": [
        "%tensorflow_version 1.x\n",
        "import tensorflow as tf \n",
        "print(tf.__version__)"
      ],
      "execution_count": 2,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "TensorFlow 1.x selected.\n",
            "1.15.2\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "JfOgVK9sZzQY",
        "colab_type": "text"
      },
      "source": [
        "**Initialize**"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "wQB26-q3ZzQZ",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "import tensorflow as tf\n",
        "import numpy as np\n",
        "import matplotlib.pyplot as plt\n",
        "import scipy.io\n",
        "from scipy import stats\n",
        "from sklearn.preprocessing import OneHotEncoder\n",
        "from sklearn.utils import resample\n",
        "from sklearn.utils import class_weight\n",
        "from sklearn.model_selection import KFold\n",
        "import time\n",
        "import os\n",
        "from google.colab import files\n",
        "import shutil\n",
        "\n",
        "read_file=scipy.io.loadmat('Example_database.mat') # Please put Example_database.mat in right position\n",
        "Input_train = np.zeros((3360,450,4))  # 80% for train data (total is 4200)\n",
        "Input_test = np.zeros((840,450,4))   # 20% for test data (total is 4200)\n",
        "Valid_set = np.zeros((840,450,4)) # Far validation set\n",
        "Valid_set_close = np.zeros((840,450,4)) # Close validation set\n",
        "\n",
        "# Assign train data from Example_database.mat\n",
        "Input_train =np.array(read_file['train_1'])\n",
        "\n",
        "# Assign test data from Example_database.mat\n",
        "Input_test =np.array(read_file['test_1'])\n",
        "\n",
        "# Assign far validation data from Example_database.mat\n",
        "Valid_set =np.array(read_file['valid_Input'])\n",
        "\n",
        "# Assign close validation data from Example_database.mat\n",
        "Valid_set_close =np.array(read_file['valid_Input_close'])\n",
        "\n",
        "train_label =np.array(read_file['train_label'])  \n",
        "test_label =np.array(read_file['test_label'])  \n",
        "valid_label = np.array(read_file['valid_label'])\n",
        "\n",
        "X_train = Input_train\n",
        "Y_train = train_label\n",
        "\n",
        "X_test = Input_test\n",
        "Y_test = test_label\n",
        "\n",
        "# Number of classes\n",
        "n_classes= 42\n",
        "# Number of features\n",
        "n_features=X_train.shape[1]\n",
        "\n"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "soPaJzZ1ZzQd",
        "colab_type": "text"
      },
      "source": [
        "**Set Target Subject**"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "E0YH-M853U--",
        "colab_type": "code",
        "outputId": "c8131343-94f8-4086-ca02-8976b920d3f8",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 34
        }
      },
      "source": [
        "ID = 1 # Set True user ID (1~42)\n",
        "\n",
        "print('ID',ID)\n",
        "\n",
        "Y_train[np.where(Y_train!=ID)] = 0\n",
        "Y_test[np.where(Y_test!=ID)] = 0\n",
        "valid_label[np.where(valid_label!=ID)] = 0"
      ],
      "execution_count": 4,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "ID 1\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "jrxsMwfzZzQg",
        "colab_type": "code",
        "outputId": "16f5d2ce-985b-48f6-ba6e-9c1a8c49e9e5",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 68
        }
      },
      "source": [
        "print(np.unique(Y_train, return_counts=True)) # Check whether train target is only 1 while others are 0\n",
        "print(np.unique(Y_test, return_counts=True)) # Check whether test target is only 1 while others are 0\n",
        "print(np.unique(valid_label, return_counts=True)) # Check whether valid target is only 1 while others are 0"
      ],
      "execution_count": 5,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "(array([0, 1], dtype=uint8), array([3283,   77]))\n",
            "(array([0, 1], dtype=uint8), array([817,  23]))\n",
            "(array([0, 1], dtype=uint8), array([820,  20]))\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "hA8DMolyZzQj",
        "colab_type": "text"
      },
      "source": [
        "**Onehot encoding + Data spliting and suffle**"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "0oXwkk8LZzQk",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "enc = OneHotEncoder()\n",
        "# randomly shuffle data before training and testing\n",
        "\n",
        "randIndx = np.arange(X_train.shape[0])\n",
        "np.random.shuffle(randIndx)\n",
        "\n",
        "randIndx2 = np.arange(X_test.shape[0])\n",
        "np.random.shuffle(randIndx2)\n",
        "\n",
        "randIndx3 = np.arange(Valid_set.shape[0])\n",
        "np.random.shuffle(randIndx3)\n",
        "\n",
        "trainSamples=np.floor(X_train.shape[0]).astype(int)\n",
        "testSamples=np.floor(X_test.shape[0]).astype(int) \n",
        "validSamples=np.floor(Valid_set.shape[0]).astype(int) \n",
        "\n",
        "X_train = X_train[randIndx,:,:] # Shuffle train data\n",
        "X_test = X_test[randIndx2,:,:] # Shuffle test data\n",
        "Valid_set = Valid_set[randIndx3,:,:] # Shuffle far validation data\n",
        "Valid_set_close = Valid_set_close[randIndx3,:,:] # Shuffle close validation data\n",
        "\n",
        "Y_train =enc.fit_transform(Y_train[randIndx].reshape(-1,1)).toarray() # Shuffle train target with one-hot encoding\n",
        "Y_test =enc.fit_transform(Y_test[randIndx2].reshape(-1,1)).toarray() # Shuffle test target with one-hot encoding\n",
        "valid_label =enc.fit_transform(valid_label[randIndx3].reshape(-1,1)).toarray() # Shuffle validation target with one-hot encoding\n"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "rEtICONyZzQq",
        "colab_type": "code",
        "outputId": "351af6e9-f227-4082-d209-82f3ddd7754f",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 51
        }
      },
      "source": [
        "# Assign to train_data (target) / test_data (target)\n",
        "train_data, train_target = X_train, Y_train\n",
        "test_data, test_target = X_test, Y_test\n",
        "\n",
        "print(train_data.shape,train_target.shape)\n",
        "print(test_data.shape,test_target.shape)\n"
      ],
      "execution_count": 7,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "(3360, 450, 4) (3360, 2)\n",
            "(840, 450, 4) (840, 2)\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "I_IE9Ys7ZzQw",
        "colab_type": "text"
      },
      "source": [
        "**Network Structure**"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "t3Rjeo_5ZzQx",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "class Model:\n",
        "\n",
        "    def __init__(self, sess, name):\n",
        "\n",
        "        self.sess = sess\n",
        "\n",
        "        self.name = name\n",
        "\n",
        "        self._build_net()\n",
        "\n",
        "    def _build_net(self):\n",
        "\n",
        "        with tf.variable_scope(self.name):\n",
        "\n",
        "            self.training = tf.placeholder(tf.bool) # For turn-on/off dropout layer\n",
        "\n",
        "            # Input place holders\n",
        "            self.X = tf.placeholder(tf.float32, [None, 450, 1]) # train/validation/test data\n",
        "            self.Y = tf.placeholder(tf.float32, [None, 2]) # train/validation/test target\n",
        "            self.learning_rate = tf.placeholder(tf.float32) # learning rate\n",
        "            self.class_weights = tf.placeholder(tf.float32) # weight value for weighted-cross entropy\n",
        "            self.reg_constant = tf.placeholder(tf.float32) # regularization constant\n",
        "\n",
        "            # Convolutional Layer #1, length: 450x1 -> 271x50 (change of feature size)\n",
        "            W1 = tf.Variable(tf.random_normal([180, 1, 50], stddev=0.01))\n",
        "            conv1 = tf.nn.conv1d(self.X, W1, stride=1, padding='VALID')\n",
        "            selu1 = tf.nn.selu(conv1) \n",
        "            dropout1 = tf.layers.dropout(inputs=selu1, rate=0.5, training=self.training)\n",
        "            \n",
        "            # Convolutional Layer #2, length: 271x50 -> 62x70 (change of feature size)\n",
        "            W2 = tf.Variable(tf.random_normal([210, 50, 70], stddev=0.01))\n",
        "            conv2 = tf.nn.conv1d(dropout1, W2, stride=1, padding='VALID')\n",
        "            selu2 = tf.nn.selu(conv2)                                    \n",
        "            dropout2 = tf.layers.dropout(inputs=selu2, rate=0.5, training=self.training)\n",
        "            \n",
        "        \n",
        "            # FC layer, length: 62x70  -> 2 output (change of feature size)\n",
        "            FC1 = tf.reshape(dropout2, [-1, 62*70])  \n",
        "            FC_weight = tf.get_variable(\"FC_weight\", shape=[62*70, 2], initializer=tf.contrib.layers.xavier_initializer())\n",
        "            logits = tf.matmul(FC1,FC_weight) # Logit value\n",
        "    \n",
        "        # Define cost/loss & optimizer \n",
        "        reg_losses = tf.nn.l2_loss(W1) + tf.nn.l2_loss(W2) + tf.nn.l2_loss(FC_weight) # L2 regularization\n",
        "        \n",
        "        self.cost = tf.reduce_mean(tf.nn.weighted_cross_entropy_with_logits(self.Y, logits, self.class_weights)) \n",
        "        self.cost = self.cost + self.reg_constant * reg_losses\n",
        "\n",
        "        self.optimizer = tf.train.AdamOptimizer(learning_rate=self.learning_rate).minimize(self.cost) \n",
        "\n",
        "        self.sigmoid_out = tf.nn.sigmoid(logits) \n",
        "        self.predict = tf.cast(self.sigmoid_out[:,1], tf.float32) # Used for getting final predictions\n",
        "                    \n",
        "        \n",
        "    def get_predict(self, x_test, training = False):          \n",
        "\n",
        "        return self.sess.run(self.predict, feed_dict={self.X: x_test, self.training: training})\n",
        "    \n",
        "\n",
        "    def get_cost(self, x_valid, y_valid, weights, reg, training = False):\n",
        "        \n",
        "        return self.sess.run(self.cost, feed_dict={self.X: x_valid, \n",
        "                                                   self.Y: y_valid, self.class_weights: weights, self.training: training, self.reg_constant: reg})\n",
        "\n",
        "\n",
        "    def train(self, x_data, y_data, weights, learning, reg, training = True):\n",
        "\n",
        "        return self.sess.run(self.optimizer, feed_dict={\n",
        "             self.X: x_data, self.Y: y_data, self.class_weights: weights, self.learning_rate: learning, self.training: training, self.reg_constant: reg})\n",
        "    "
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "K-GFm7kdZzQz",
        "colab_type": "text"
      },
      "source": [
        "**Main**"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "scrolled": false,
        "id": "ak_o620MZzQz",
        "colab_type": "code",
        "outputId": "9a53261f-79b0-4e31-f78a-0f5cbf6bf480",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 666
        }
      },
      "source": [
        "### Hyperparameters\n",
        "\n",
        "learning_rate = 0.0001\n",
        "training_epochs = 60\n",
        "batch_size = 336 # Mini-batch size: total: 3360(train)/840(test) \n",
        "num_cv = 10  # 10-fold cross validation\n",
        "bag_size = 3024  # Bag_size = train_data_size - batch_size\n",
        "ear_epoch = 0\n",
        "\n",
        "valid_cost = np.zeros((training_epochs,4))\n",
        "\n",
        "# initialize\n",
        "sess = tf.Session()\n",
        "\n",
        "models = []\n",
        "num_models = 4 \n",
        "\n",
        "for m in range(num_models):\n",
        "    print('Number of Model {}'.format(m+1))\n",
        "    models.append(Model(sess, \"model\" + str(m)))    \n",
        "\n",
        "\n",
        "sess.run(tf.global_variables_initializer())\n",
        "\n",
        "\n",
        "start_time = time.time() # Time starts!\n",
        "\n",
        "print('Learning Started!')\n",
        "\n",
        "# Train model\n",
        "for epoch in range(training_epochs):\n",
        "         \n",
        "    total_batch = int(bag_size / batch_size)\n",
        "    valid_cost_cv = [0,0,0,0]\n",
        "    \n",
        "    cv = KFold(n_splits=num_cv, shuffle=True)\n",
        "    for train_index, test_index in cv.split(train_data): # For cross validation\n",
        "        cv_train_data, cv_validate_data = train_data[train_index], train_data[test_index]\n",
        "        cv_train_target, cv_validate_target = train_target[train_index], train_target[test_index]    \n",
        "                    \n",
        "        for m_idx, m in enumerate(models):\n",
        "            # Assign different regularization constant. They are best hyperparameters from our searching.\n",
        "            if m_idx==0: # DW feature\n",
        "              training, validating = cv_train_data[:,:,0], cv_validate_data[:,:,0]\n",
        "              training_target, validating_target = cv_train_target, cv_validate_target\n",
        "              reg = 0.02 \n",
        "            if m_idx==1: # TP feature\n",
        "              training, validating = cv_train_data[:,:,1], cv_validate_data[:,:,1]\n",
        "              training_target, validating_target = cv_train_target, cv_validate_target\n",
        "              reg = 0.01            \n",
        "            if m_idx==2: # FP feature\n",
        "              training, validating = cv_train_data[:,:,2], cv_validate_data[:,:,2]\n",
        "              training_target, validating_target = cv_train_target, cv_validate_target            \n",
        "              reg = 0.02\n",
        "            if m_idx==3: # Cubic feature\n",
        "              training, validating = cv_train_data[:,:,3], cv_validate_data[:,:,3]\n",
        "              training_target, validating_target = cv_train_target, cv_validate_target            \n",
        "              reg = 0.01   \n",
        "\n",
        "            training = np.reshape(training,[len(training),n_features,1])\n",
        "            validating = np.reshape(validating,[len(validating),n_features,1])\n",
        "\n",
        "            for i in range(total_batch):\n",
        "\n",
        "                batch_xs, batch_ys = training[batch_size*i:batch_size*(i+1),:,:], training_target[batch_size*i:batch_size*(i+1)]                \n",
        "                pos_weight = (n_classes - 1) # Weight value for weighted-cross entropy\n",
        "\n",
        "                _ = m.train(batch_xs, batch_ys, pos_weight, learning_rate, reg)  # Train model independently\n",
        "            \n",
        "            pos_weight = 1 # Usual cross entropy for validation/test set\n",
        "            valid_cost_cv[m_idx] += m.get_cost(validating, validating_target, pos_weight, reg) \n",
        "            \n",
        "    valid_cost[epoch] =np.divide(valid_cost_cv ,num_cv) # Cost for cross-validation\n",
        "    \n",
        "    if ((epoch+1)%5) == 0:\n",
        "        print('Epoch:', '%04d' % (epoch + 1), 'Model 0_cost =', round(valid_cost[epoch,0],3),' Model 1_cost =', round(valid_cost[epoch,1],3),' Model 2_cost =', round(valid_cost[epoch,2],3),' Model 3_cost =', round(valid_cost[epoch,3],3)) \n",
        "       \n",
        "        \n",
        "print('Learning Finished!')\n",
        "\n",
        "end_time = time.time() # Time ends!\n",
        "\n",
        "train_time = end_time - start_time\n",
        "\n",
        "print('Training time: {:.2f} seconds'.format(train_time))\n",
        "print('Last validation error for DW: {:.3f} '.format(valid_cost[-1,0]))\n",
        "print('Last validation error for TP: {:.3f} '.format(valid_cost[-1,1]))\n",
        "print('Last validation error for FP: {:.3f} '.format(valid_cost[-1,2]))\n",
        "print('Last validation error for Cubic: {:.3f} '.format(valid_cost[-1,3]))\n"
      ],
      "execution_count": 9,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Number of Model 1\n",
            "WARNING:tensorflow:From <ipython-input-8-c0efac745646>:28: dropout (from tensorflow.python.layers.core) is deprecated and will be removed in a future version.\n",
            "Instructions for updating:\n",
            "Use keras.layers.dropout instead.\n",
            "WARNING:tensorflow:From /tensorflow-1.15.2/python3.6/tensorflow_core/python/layers/core.py:271: Layer.apply (from tensorflow.python.keras.engine.base_layer) is deprecated and will be removed in a future version.\n",
            "Instructions for updating:\n",
            "Please use `layer.__call__` method instead.\n",
            "WARNING:tensorflow:\n",
            "The TensorFlow contrib module will not be included in TensorFlow 2.0.\n",
            "For more information, please see:\n",
            "  * https://github.com/tensorflow/community/blob/master/rfcs/20180907-contrib-sunset.md\n",
            "  * https://github.com/tensorflow/addons\n",
            "  * https://github.com/tensorflow/io (for I/O related ops)\n",
            "If you depend on functionality not listed there, please file an issue.\n",
            "\n",
            "Number of Model 2\n",
            "Number of Model 3\n",
            "Number of Model 4\n",
            "Learning Started!\n",
            "Epoch: 0005 Model 0_cost = 0.763  Model 1_cost = 0.662  Model 2_cost = 0.77  Model 3_cost = 0.449\n",
            "Epoch: 0010 Model 0_cost = 0.5  Model 1_cost = 0.455  Model 2_cost = 0.55  Model 3_cost = 0.33\n",
            "Epoch: 0015 Model 0_cost = 0.325  Model 1_cost = 0.349  Model 2_cost = 0.4  Model 3_cost = 0.261\n",
            "Epoch: 0020 Model 0_cost = 0.22  Model 1_cost = 0.27  Model 2_cost = 0.292  Model 3_cost = 0.206\n",
            "Epoch: 0025 Model 0_cost = 0.157  Model 1_cost = 0.212  Model 2_cost = 0.215  Model 3_cost = 0.162\n",
            "Epoch: 0030 Model 0_cost = 0.117  Model 1_cost = 0.176  Model 2_cost = 0.162  Model 3_cost = 0.122\n",
            "Epoch: 0035 Model 0_cost = 0.094  Model 1_cost = 0.152  Model 2_cost = 0.127  Model 3_cost = 0.095\n",
            "Epoch: 0040 Model 0_cost = 0.076  Model 1_cost = 0.135  Model 2_cost = 0.099  Model 3_cost = 0.072\n",
            "Epoch: 0045 Model 0_cost = 0.066  Model 1_cost = 0.126  Model 2_cost = 0.076  Model 3_cost = 0.056\n",
            "Epoch: 0050 Model 0_cost = 0.059  Model 1_cost = 0.118  Model 2_cost = 0.062  Model 3_cost = 0.043\n",
            "Epoch: 0055 Model 0_cost = 0.055  Model 1_cost = 0.113  Model 2_cost = 0.052  Model 3_cost = 0.037\n",
            "Epoch: 0060 Model 0_cost = 0.052  Model 1_cost = 0.11  Model 2_cost = 0.046  Model 3_cost = 0.028\n",
            "Learning Finished!\n",
            "Training time: 347.93 seconds\n",
            "Last validation error for DW: 0.052 \n",
            "Last validation error for TP: 0.110 \n",
            "Last validation error for FP: 0.046 \n",
            "Last validation error for Cubic: 0.028 \n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "7IQ-uF8vZzQ2",
        "colab_type": "text"
      },
      "source": [
        "**Epoch vs Cost plot**"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "ySwsMlKeZzQ2",
        "colab_type": "code",
        "outputId": "3c64ee83-c889-4c01-9870-3590119022ec",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 295
        }
      },
      "source": [
        "fig = plt.figure()\n",
        "ax = plt.subplot(111)\n",
        "\n",
        "x_axis = np.linspace(1.0, training_epochs, num=training_epochs)\n",
        "ax.plot(x_axis, valid_cost[:,0],'r', label='DW feature')\n",
        "ax.plot(x_axis, valid_cost[:,1],'g', label='TP feature')\n",
        "ax.plot(x_axis, valid_cost[:,2],'b', label='FP feature')\n",
        "ax.plot(x_axis, valid_cost[:,3],'m', label='Cubic feature')\n",
        "\n",
        "ax.legend()\n",
        "plt.title('Epoch vs Cost')\n",
        "plt.xlabel('Epoch')\n",
        "plt.ylabel('Cost')\n",
        "\n",
        "plt.show()"
      ],
      "execution_count": 10,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "image/png": "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\n",
            "text/plain": [
              "<Figure size 432x288 with 1 Axes>"
            ]
          },
          "metadata": {
            "tags": [],
            "needs_background": "light"
          }
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "AyENg8npAICE",
        "colab_type": "text"
      },
      "source": [
        "**Performance from far and close validation sets**\n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "bQQ-ZPWKAewI",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "thres = np.arange(0, 1.001, 0.001)\n",
        "\n",
        "valid_0 = np.reshape(Valid_set[:,:,0], [len(Valid_set[:,:,0]),n_features,1])\n",
        "valid_1 = np.reshape(Valid_set[:,:,1], [len(Valid_set[:,:,1]),n_features,1])\n",
        "valid_2 = np.reshape(Valid_set[:,:,2], [len(Valid_set[:,:,2]),n_features,1])\n",
        "valid_3 = np.reshape(Valid_set[:,:,3], [len(Valid_set[:,:,3]),n_features,1])\n",
        "\n",
        "valid_close_0 = np.reshape(Valid_set_close[:,:,0], [len(Valid_set_close[:,:,0]),n_features,1])\n",
        "valid_close_1 = np.reshape(Valid_set_close[:,:,1], [len(Valid_set_close[:,:,1]),n_features,1])\n",
        "valid_close_2 = np.reshape(Valid_set_close[:,:,2], [len(Valid_set_close[:,:,2]),n_features,1])\n",
        "valid_close_3 = np.reshape(Valid_set_close[:,:,3], [len(Valid_set_close[:,:,3]),n_features,1])\n",
        "\n",
        "start_time_valid = time.time() # Time starts!    \n",
        "\n",
        "valid_logit_0 =  models[0].get_predict(valid_0) # Far-validation score in Model-DW feature\n",
        "valid_logit_1 =  models[1].get_predict(valid_1) # Far-validation score in Model-TP feature\n",
        "valid_logit_2 =  models[2].get_predict(valid_2) # Far-validation score in Model-FP feature\n",
        "valid_logit_3 =  models[3].get_predict(valid_3) # Far-validation score in Model-Cubic feature\n",
        "\n",
        "valid_close_logit_0 =  models[0].get_predict(valid_close_0) # Close-validation score in Model-DW feature\n",
        "valid_close_logit_1 =  models[1].get_predict(valid_close_1) # Close-validation score in Model-TP feature\n",
        "valid_close_logit_2 =  models[2].get_predict(valid_close_2) # Close-validation score in Model-FP feature\n",
        "valid_close_logit_3 =  models[3].get_predict(valid_close_3) # Close-validation score in Model-Cubic feature\n"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "Mst-SY8jAG2m",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "# Far-validation result in Model-DW feature\n",
        "\n",
        "thres_matrix_valid_0 = np.zeros((len(valid_label),len(thres)))\n",
        "total_matrix_valid_0 = np.zeros((len(thres),4)) #acc,far,frr,recall(order of column)\n",
        "\n",
        "\n",
        "for j in range(len(thres)):\n",
        "    for i in range(len(valid_label)):\n",
        "        if valid_logit_0[i] < thres[j]:\n",
        "            thres_matrix_valid_0[i,j] = 0\n",
        "        else:\n",
        "            thres_matrix_valid_0[i,j] = 1\n",
        "\n",
        "for i in range(len(thres)):\n",
        "\n",
        "    equal_logit = np.equal(thres_matrix_valid_0[:,i], valid_label[:,1])\n",
        "    unique2, True_pos_neg_test = np.unique(thres_matrix_valid_0[np.where(equal_logit==True),i], return_counts=True)\n",
        "    unique3, False_pos_neg_test = np.unique(thres_matrix_valid_0[np.where(equal_logit==False),i], return_counts=True) \n",
        "\n",
        "    if np.shape(True_pos_neg_test)==(1,):\n",
        "        if unique2[0] == 0:\n",
        "            True_pos_neg_test = [True_pos_neg_test[0], 0]\n",
        "        else:\n",
        "            True_pos_neg_test = [0, True_pos_neg_test[0]]\n",
        "\n",
        "    if np.shape(False_pos_neg_test)==(1,):\n",
        "        if unique3[0] == 0:\n",
        "            False_pos_neg_test = [False_pos_neg_test[0], 0]\n",
        "        else:\n",
        "            False_pos_neg_test = [0, False_pos_neg_test[0]]\n",
        "\n",
        "    if np.shape(True_pos_neg_test)==(0,):\n",
        "        True_pos_neg_test = [0, 0]\n",
        "\n",
        "    if np.shape(False_pos_neg_test)==(0,):\n",
        "        False_pos_neg_test = [0, 0]\n",
        "\n",
        "    Recall_test= True_pos_neg_test[1]/(False_pos_neg_test[0]+True_pos_neg_test[1])\n",
        "    Specific_test = True_pos_neg_test[0] /(True_pos_neg_test[0]+False_pos_neg_test[1])\n",
        "\n",
        "    ACC_test = (True_pos_neg_test[0]+True_pos_neg_test[1]) / (len(valid_label))\n",
        "    FAR_test = 1 - Specific_test\n",
        "    FRR_test = 1 - Recall_test\n",
        "\n",
        "    total_matrix_valid_0[i,:] = ACC_test, FAR_test, FRR_test, Recall_test\n",
        "\n",
        "\n",
        "EER_loc_test = np.argmin(abs(total_matrix_valid_0[:,1] - total_matrix_valid_0[:,2]))\n",
        "EER_0 = (total_matrix_valid_0[EER_loc_test,1]+total_matrix_valid_0[EER_loc_test,2])/2"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "3eCpdItGAG5j",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "# Far-validation result in Model-TP feature\n",
        "\n",
        "thres_matrix_valid_1 = np.zeros((len(valid_label),len(thres)))\n",
        "total_matrix_valid_1 = np.zeros((len(thres),4)) #acc,far,frr,recall(order of column)\n",
        "\n",
        "\n",
        "for j in range(len(thres)):\n",
        "    for i in range(len(valid_label)):\n",
        "        if valid_logit_1[i] < thres[j]:\n",
        "            thres_matrix_valid_1[i,j] = 0\n",
        "        else:\n",
        "            thres_matrix_valid_1[i,j] = 1\n",
        "\n",
        "for i in range(len(thres)):\n",
        "\n",
        "    equal_logit = np.equal(thres_matrix_valid_1[:,i], valid_label[:,1]) \n",
        "    unique2, True_pos_neg_test = np.unique(thres_matrix_valid_1[np.where(equal_logit==True),i], return_counts=True)\n",
        "    unique3, False_pos_neg_test = np.unique(thres_matrix_valid_1[np.where(equal_logit==False),i], return_counts=True) \n",
        "\n",
        "    if np.shape(True_pos_neg_test)==(1,):\n",
        "        if unique2[0] == 0:\n",
        "            True_pos_neg_test = [True_pos_neg_test[0], 0]\n",
        "        else:\n",
        "            True_pos_neg_test = [0, True_pos_neg_test[0]]\n",
        "\n",
        "    if np.shape(False_pos_neg_test)==(1,):\n",
        "        if unique3[0] == 0:\n",
        "            False_pos_neg_test = [False_pos_neg_test[0], 0]\n",
        "        else:\n",
        "            False_pos_neg_test = [0, False_pos_neg_test[0]]\n",
        "\n",
        "    if np.shape(True_pos_neg_test)==(0,):\n",
        "        True_pos_neg_test = [0, 0]\n",
        "\n",
        "    if np.shape(False_pos_neg_test)==(0,):\n",
        "        False_pos_neg_test = [0, 0]\n",
        "\n",
        "    Recall_test= True_pos_neg_test[1]/(False_pos_neg_test[0]+True_pos_neg_test[1])\n",
        "    Specific_test = True_pos_neg_test[0] /(True_pos_neg_test[0]+False_pos_neg_test[1])\n",
        "\n",
        "    ACC_test = (True_pos_neg_test[0]+True_pos_neg_test[1]) / (len(valid_label))\n",
        "    FAR_test = 1 - Specific_test\n",
        "    FRR_test = 1 - Recall_test\n",
        "\n",
        "    total_matrix_valid_1[i,:] = ACC_test, FAR_test, FRR_test, Recall_test\n",
        "\n",
        "EER_loc_test = np.argmin(abs(total_matrix_valid_1[:,1] - total_matrix_valid_1[:,2]))\n",
        "EER_1 = (total_matrix_valid_1[EER_loc_test,1]+total_matrix_valid_1[EER_loc_test,2])/2"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "oXQ8-oHiAG8X",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "# Far-validation result in Model-FP feature\n",
        "\n",
        "thres_matrix_valid_2 = np.zeros((len(valid_label),len(thres)))\n",
        "total_matrix_valid_2 = np.zeros((len(thres),4)) #acc,far,frr,recall(order of column)\n",
        "\n",
        "\n",
        "for j in range(len(thres)):\n",
        "    for i in range(len(valid_label)):\n",
        "        if valid_logit_2[i] < thres[j]:\n",
        "            thres_matrix_valid_2[i,j] = 0\n",
        "        else:\n",
        "            thres_matrix_valid_2[i,j] = 1\n",
        "\n",
        "for i in range(len(thres)):\n",
        "\n",
        "    equal_logit = np.equal(thres_matrix_valid_2[:,i], valid_label[:,1]) \n",
        "    unique2, True_pos_neg_test = np.unique(thres_matrix_valid_2[np.where(equal_logit==True),i], return_counts=True)\n",
        "    unique3, False_pos_neg_test = np.unique(thres_matrix_valid_2[np.where(equal_logit==False),i], return_counts=True) \n",
        "\n",
        "    if np.shape(True_pos_neg_test)==(1,):\n",
        "        if unique2[0] == 0:\n",
        "            True_pos_neg_test = [True_pos_neg_test[0], 0]\n",
        "        else:\n",
        "            True_pos_neg_test = [0, True_pos_neg_test[0]]\n",
        "\n",
        "    if np.shape(False_pos_neg_test)==(1,):\n",
        "        if unique3[0] == 0:\n",
        "            False_pos_neg_test = [False_pos_neg_test[0], 0]\n",
        "        else:\n",
        "            False_pos_neg_test = [0, False_pos_neg_test[0]]\n",
        "\n",
        "    if np.shape(True_pos_neg_test)==(0,):\n",
        "        True_pos_neg_test = [0, 0]\n",
        "\n",
        "    if np.shape(False_pos_neg_test)==(0,):\n",
        "        False_pos_neg_test = [0, 0]\n",
        "\n",
        "    Recall_test= True_pos_neg_test[1]/(False_pos_neg_test[0]+True_pos_neg_test[1])\n",
        "    Specific_test = True_pos_neg_test[0] /(True_pos_neg_test[0]+False_pos_neg_test[1])\n",
        "\n",
        "    ACC_test = (True_pos_neg_test[0]+True_pos_neg_test[1]) / (len(valid_label))\n",
        "    FAR_test = 1 - Specific_test\n",
        "    FRR_test = 1 - Recall_test\n",
        "\n",
        "    total_matrix_valid_2[i,:] = ACC_test, FAR_test, FRR_test, Recall_test\n",
        "\n",
        "EER_loc_test = np.argmin(abs(total_matrix_valid_2[:,1] - total_matrix_valid_2[:,2]))\n",
        "EER_2 = (total_matrix_valid_2[EER_loc_test,1]+total_matrix_valid_2[EER_loc_test,2])/2"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "0LQ9YCSyYKH5",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "# Far-validation result in Model-Cubic feature\n",
        "\n",
        "thres_matrix_valid_3 = np.zeros((len(valid_label),len(thres)))\n",
        "total_matrix_valid_3 = np.zeros((len(thres),4)) #acc,far,frr,recall(order of column)\n",
        "\n",
        "\n",
        "for j in range(len(thres)):\n",
        "    for i in range(len(valid_label)):\n",
        "        if valid_logit_3[i] < thres[j]:\n",
        "            thres_matrix_valid_3[i,j] = 0\n",
        "        else:\n",
        "            thres_matrix_valid_3[i,j] = 1\n",
        "\n",
        "for i in range(len(thres)):\n",
        "\n",
        "    equal_logit = np.equal(thres_matrix_valid_3[:,i], valid_label[:,1]) \n",
        "    unique2, True_pos_neg_test = np.unique(thres_matrix_valid_3[np.where(equal_logit==True),i], return_counts=True)\n",
        "    unique3, False_pos_neg_test = np.unique(thres_matrix_valid_3[np.where(equal_logit==False),i], return_counts=True) \n",
        "\n",
        "    if np.shape(True_pos_neg_test)==(1,):\n",
        "        if unique2[0] == 0:\n",
        "            True_pos_neg_test = [True_pos_neg_test[0], 0]\n",
        "        else:\n",
        "            True_pos_neg_test = [0, True_pos_neg_test[0]]\n",
        "\n",
        "    if np.shape(False_pos_neg_test)==(1,):\n",
        "        if unique3[0] == 0:\n",
        "            False_pos_neg_test = [False_pos_neg_test[0], 0]\n",
        "        else:\n",
        "            False_pos_neg_test = [0, False_pos_neg_test[0]]\n",
        "\n",
        "    if np.shape(True_pos_neg_test)==(0,):\n",
        "        True_pos_neg_test = [0, 0]\n",
        "\n",
        "    if np.shape(False_pos_neg_test)==(0,):\n",
        "        False_pos_neg_test = [0, 0]\n",
        "\n",
        "    Recall_test= True_pos_neg_test[1]/(False_pos_neg_test[0]+True_pos_neg_test[1])\n",
        "    Specific_test = True_pos_neg_test[0] /(True_pos_neg_test[0]+False_pos_neg_test[1])\n",
        "\n",
        "    ACC_test = (True_pos_neg_test[0]+True_pos_neg_test[1]) / (len(valid_label))\n",
        "    FAR_test = 1 - Specific_test\n",
        "    FRR_test = 1 - Recall_test\n",
        "\n",
        "    total_matrix_valid_3[i,:] = ACC_test, FAR_test, FRR_test, Recall_test\n",
        "\n",
        "EER_loc_test = np.argmin(abs(total_matrix_valid_3[:,1] - total_matrix_valid_3[:,2]))\n",
        "EER_3 = (total_matrix_valid_3[EER_loc_test,1]+total_matrix_valid_3[EER_loc_test,2])/2\n",
        "\n",
        "valid_per = [EER_0, EER_1, EER_2, EER_3] # EER results from far-validation set in each model\n",
        "\n",
        "for i in range(len(valid_per)): # If EER is 0, give 0.001 instead to avoid NaN\n",
        "\tif valid_per[i] ==0:\n",
        "\t\tvalid_per[i] = 0.001\n"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "-16Ek78OpDuK",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "# Close-validation result in Model-DW feature\n",
        "\n",
        "thres_matrix_valid_close_0 = np.zeros((len(valid_label),len(thres)))\n",
        "total_matrix_valid_close_0 = np.zeros((len(thres),4)) #acc,far,frr,recall(order of column)\n",
        "\n",
        "\n",
        "for j in range(len(thres)):\n",
        "    for i in range(len(valid_label)):\n",
        "        if valid_close_logit_0[i] < thres[j]:\n",
        "            thres_matrix_valid_close_0[i,j] = 0\n",
        "        else:\n",
        "            thres_matrix_valid_close_0[i,j] = 1\n",
        "\n",
        "for i in range(len(thres)):\n",
        "\n",
        "    equal_logit = np.equal(thres_matrix_valid_close_0[:,i], valid_label[:,1]) \n",
        "    unique2, True_pos_neg_test = np.unique(thres_matrix_valid_close_0[np.where(equal_logit==True),i], return_counts=True)\n",
        "    unique3, False_pos_neg_test = np.unique(thres_matrix_valid_close_0[np.where(equal_logit==False),i], return_counts=True) \n",
        "\n",
        "    if np.shape(True_pos_neg_test)==(1,):\n",
        "        if unique2[0] == 0:\n",
        "            True_pos_neg_test = [True_pos_neg_test[0], 0]\n",
        "        else:\n",
        "            True_pos_neg_test = [0, True_pos_neg_test[0]]\n",
        "\n",
        "    if np.shape(False_pos_neg_test)==(1,):\n",
        "        if unique3[0] == 0:\n",
        "            False_pos_neg_test = [False_pos_neg_test[0], 0]\n",
        "        else:\n",
        "            False_pos_neg_test = [0, False_pos_neg_test[0]]\n",
        "\n",
        "    if np.shape(True_pos_neg_test)==(0,):\n",
        "        True_pos_neg_test = [0, 0]\n",
        "\n",
        "    if np.shape(False_pos_neg_test)==(0,):\n",
        "        False_pos_neg_test = [0, 0]\n",
        "\n",
        "    Recall_test= True_pos_neg_test[1]/(False_pos_neg_test[0]+True_pos_neg_test[1])\n",
        "    Specific_test = True_pos_neg_test[0] /(True_pos_neg_test[0]+False_pos_neg_test[1])\n",
        "\n",
        "    ACC_test = (True_pos_neg_test[0]+True_pos_neg_test[1]) / (len(valid_label))\n",
        "    FAR_test = 1 - Specific_test\n",
        "    FRR_test = 1 - Recall_test\n",
        "\n",
        "    total_matrix_valid_close_0[i,:] = ACC_test, FAR_test, FRR_test, Recall_test\n",
        "\n",
        "\n",
        "EER_loc_test = np.argmin(abs(total_matrix_valid_close_0[:,1] - total_matrix_valid_close_0[:,2]))\n",
        "EER_close_0 = (total_matrix_valid_close_0[EER_loc_test,1]+total_matrix_valid_close_0[EER_loc_test,2])/2\n"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "XA189l9VpECx",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "# Close-validation result in Model-TP feature\n",
        "\n",
        "thres_matrix_valid_close_1 = np.zeros((len(valid_label),len(thres)))\n",
        "total_matrix_valid_close_1 = np.zeros((len(thres),4)) #acc,far,frr,recall(order of column)\n",
        "\n",
        "\n",
        "for j in range(len(thres)):\n",
        "    for i in range(len(valid_label)):\n",
        "        if valid_close_logit_1[i] < thres[j]:\n",
        "            thres_matrix_valid_close_1[i,j] = 0\n",
        "        else:\n",
        "            thres_matrix_valid_close_1[i,j] = 1\n",
        "\n",
        "for i in range(len(thres)):\n",
        "\n",
        "    equal_logit = np.equal(thres_matrix_valid_close_1[:,i], valid_label[:,1])\n",
        "    unique2, True_pos_neg_test = np.unique(thres_matrix_valid_close_1[np.where(equal_logit==True),i], return_counts=True)\n",
        "    unique3, False_pos_neg_test = np.unique(thres_matrix_valid_close_1[np.where(equal_logit==False),i], return_counts=True) \n",
        "\n",
        "    if np.shape(True_pos_neg_test)==(1,):\n",
        "        if unique2[0] == 0:\n",
        "            True_pos_neg_test = [True_pos_neg_test[0], 0]\n",
        "        else:\n",
        "            True_pos_neg_test = [0, True_pos_neg_test[0]]\n",
        "\n",
        "    if np.shape(False_pos_neg_test)==(1,):\n",
        "        if unique3[0] == 0:\n",
        "            False_pos_neg_test = [False_pos_neg_test[0], 0]\n",
        "        else:\n",
        "            False_pos_neg_test = [0, False_pos_neg_test[0]]\n",
        "\n",
        "    if np.shape(True_pos_neg_test)==(0,):\n",
        "        True_pos_neg_test = [0, 0]\n",
        "\n",
        "    if np.shape(False_pos_neg_test)==(0,):\n",
        "        False_pos_neg_test = [0, 0]\n",
        "\n",
        "    Recall_test= True_pos_neg_test[1]/(False_pos_neg_test[0]+True_pos_neg_test[1])\n",
        "    Specific_test = True_pos_neg_test[0] /(True_pos_neg_test[0]+False_pos_neg_test[1])\n",
        "\n",
        "    ACC_test = (True_pos_neg_test[0]+True_pos_neg_test[1]) / (len(valid_label))\n",
        "    FAR_test = 1 - Specific_test\n",
        "    FRR_test = 1 - Recall_test\n",
        "\n",
        "    total_matrix_valid_close_1[i,:] = ACC_test, FAR_test, FRR_test, Recall_test\n",
        "\n",
        "EER_loc_test = np.argmin(abs(total_matrix_valid_close_1[:,1] - total_matrix_valid_close_1[:,2]))\n",
        "EER_close_1 = (total_matrix_valid_close_1[EER_loc_test,1]+total_matrix_valid_close_1[EER_loc_test,2])/2"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "AOIqH5BHpEKQ",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "# Close-validation result in Model-FP feature\n",
        "\n",
        "thres_matrix_valid_close_2 = np.zeros((len(valid_label),len(thres)))\n",
        "total_matrix_valid_close_2 = np.zeros((len(thres),4)) #acc,far,frr,recall(order of column)\n",
        "\n",
        "\n",
        "for j in range(len(thres)):\n",
        "    for i in range(len(valid_label)):\n",
        "        if valid_close_logit_2[i] < thres[j]:\n",
        "            thres_matrix_valid_close_2[i,j] = 0\n",
        "        else:\n",
        "            thres_matrix_valid_close_2[i,j] = 1\n",
        "\n",
        "for i in range(len(thres)):\n",
        "\n",
        "    equal_logit = np.equal(thres_matrix_valid_close_2[:,i], valid_label[:,1])\n",
        "    unique2, True_pos_neg_test = np.unique(thres_matrix_valid_close_2[np.where(equal_logit==True),i], return_counts=True)\n",
        "    unique3, False_pos_neg_test = np.unique(thres_matrix_valid_close_2[np.where(equal_logit==False),i], return_counts=True) \n",
        "\n",
        "    if np.shape(True_pos_neg_test)==(1,):\n",
        "        if unique2[0] == 0:\n",
        "            True_pos_neg_test = [True_pos_neg_test[0], 0]\n",
        "        else:\n",
        "            True_pos_neg_test = [0, True_pos_neg_test[0]]\n",
        "\n",
        "    if np.shape(False_pos_neg_test)==(1,):\n",
        "        if unique3[0] == 0:\n",
        "            False_pos_neg_test = [False_pos_neg_test[0], 0]\n",
        "        else:\n",
        "            False_pos_neg_test = [0, False_pos_neg_test[0]]\n",
        "\n",
        "    if np.shape(True_pos_neg_test)==(0,):\n",
        "        True_pos_neg_test = [0, 0]\n",
        "\n",
        "    if np.shape(False_pos_neg_test)==(0,):\n",
        "        False_pos_neg_test = [0, 0]\n",
        "\n",
        "    Recall_test= True_pos_neg_test[1]/(False_pos_neg_test[0]+True_pos_neg_test[1])\n",
        "    Specific_test = True_pos_neg_test[0] /(True_pos_neg_test[0]+False_pos_neg_test[1])\n",
        "\n",
        "    ACC_test = (True_pos_neg_test[0]+True_pos_neg_test[1]) / (len(valid_label))\n",
        "    FAR_test = 1 - Specific_test\n",
        "    FRR_test = 1 - Recall_test\n",
        "\n",
        "    total_matrix_valid_close_2[i,:] = ACC_test, FAR_test, FRR_test, Recall_test\n",
        "\n",
        "EER_loc_test = np.argmin(abs(total_matrix_valid_close_2[:,1] - total_matrix_valid_close_2[:,2]))\n",
        "EER_close_2 = (total_matrix_valid_close_2[EER_loc_test,1]+total_matrix_valid_close_2[EER_loc_test,2])/2"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "uTuksQJTpERP",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "# Close-validation result in Model-Cubic feature\n",
        "\n",
        "thres_matrix_valid_close_3 = np.zeros((len(valid_label),len(thres)))\n",
        "total_matrix_valid_close_3 = np.zeros((len(thres),4)) #acc,far,frr,recall(order of column)\n",
        "\n",
        "\n",
        "for j in range(len(thres)):\n",
        "    for i in range(len(valid_label)):\n",
        "        if valid_close_logit_3[i] < thres[j]:\n",
        "            thres_matrix_valid_close_3[i,j] = 0\n",
        "        else:\n",
        "            thres_matrix_valid_close_3[i,j] = 1\n",
        "\n",
        "for i in range(len(thres)):\n",
        "\n",
        "    equal_logit = np.equal(thres_matrix_valid_close_3[:,i], valid_label[:,1])\n",
        "    unique2, True_pos_neg_test = np.unique(thres_matrix_valid_close_3[np.where(equal_logit==True),i], return_counts=True)\n",
        "    unique3, False_pos_neg_test = np.unique(thres_matrix_valid_close_3[np.where(equal_logit==False),i], return_counts=True) \n",
        "\n",
        "    if np.shape(True_pos_neg_test)==(1,):\n",
        "        if unique2[0] == 0:\n",
        "            True_pos_neg_test = [True_pos_neg_test[0], 0]\n",
        "        else:\n",
        "            True_pos_neg_test = [0, True_pos_neg_test[0]]\n",
        "\n",
        "    if np.shape(False_pos_neg_test)==(1,):\n",
        "        if unique3[0] == 0:\n",
        "            False_pos_neg_test = [False_pos_neg_test[0], 0]\n",
        "        else:\n",
        "            False_pos_neg_test = [0, False_pos_neg_test[0]]\n",
        "\n",
        "    if np.shape(True_pos_neg_test)==(0,):\n",
        "        True_pos_neg_test = [0, 0]\n",
        "\n",
        "    if np.shape(False_pos_neg_test)==(0,):\n",
        "        False_pos_neg_test = [0, 0]\n",
        "\n",
        "    Recall_test= True_pos_neg_test[1]/(False_pos_neg_test[0]+True_pos_neg_test[1])\n",
        "    Specific_test = True_pos_neg_test[0] /(True_pos_neg_test[0]+False_pos_neg_test[1])\n",
        "\n",
        "    ACC_test = (True_pos_neg_test[0]+True_pos_neg_test[1]) / (len(valid_label))\n",
        "    FAR_test = 1 - Specific_test\n",
        "    FRR_test = 1 - Recall_test\n",
        "\n",
        "    total_matrix_valid_close_3[i,:] = ACC_test, FAR_test, FRR_test, Recall_test\n",
        "\n",
        "EER_loc_test = np.argmin(abs(total_matrix_valid_close_3[:,1] - total_matrix_valid_close_3[:,2]))\n",
        "EER_close_3 = (total_matrix_valid_close_3[EER_loc_test,1]+total_matrix_valid_close_3[EER_loc_test,2])/2\n",
        "\n",
        "# EER results from close-validation set in each model\n",
        "valid_close_per = [EER_close_0, EER_close_1, EER_close_2, EER_close_3] \n",
        "\t\n",
        "for i in range(len(valid_close_per)): # If EER is 0, give 0.001 instead to avoid NaN\n",
        "\tif valid_close_per[i] ==0:\n",
        "\t\tvalid_close_per[i] = 0.001\t\t\n",
        "\n",
        "valid_time = time.time() - start_time_valid # Time for calculating far and close validation sets"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "AzIlfY14Bk63",
        "colab_type": "text"
      },
      "source": [
        "**Results of test set in individual model**"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "xfGvATHEBdh-",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "testing_0 = np.reshape(test_data[:,:,0], [len(test_data[:,:,0]),n_features,1])\n",
        "testing_1 = np.reshape(test_data[:,:,1], [len(test_data[:,:,1]),n_features,1])\n",
        "testing_2 = np.reshape(test_data[:,:,2], [len(test_data[:,:,2]),n_features,1])\n",
        "testing_3 = np.reshape(test_data[:,:,3], [len(test_data[:,:,3]),n_features,1])\n",
        "\n",
        "start_time_test2 = time.time() # Time starts!    \n",
        "\n",
        "test_logit_0 =  models[0].get_predict(testing_0) # Test score in Model-DW feature\n",
        "test_logit_1 =  models[1].get_predict(testing_1) # Test score in Model-TP feature\n",
        "test_logit_2 =  models[2].get_predict(testing_2) # Test score in Model-FP feature\n",
        "test_logit_3 =  models[3].get_predict(testing_3) # Test score in Model-Cubic feature    \n",
        "\n",
        "test_all_logit = np.array([test_logit_0, test_logit_1, test_logit_2, test_logit_3])\n",
        "\n",
        "test_time = time.time() - start_time_test2  # Time for predicting in each model"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "m8uN8WwrrbMd",
        "colab_type": "text"
      },
      "source": [
        "**Variation-stable approach with assigning weights**"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "3sFa-D5brV30",
        "colab_type": "code",
        "outputId": "21ff0490-3753-4882-d38d-818476c6c5de",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 51
        }
      },
      "source": [
        "start_time_vari = time.time() # Time starts!    \n",
        "\n",
        "diff_value = abs(np.subtract(valid_per,valid_close_per)) # Calculate the difference between validation sets\n",
        "\n",
        "vari_valid_per = []\n",
        "vari_loc = []\n",
        "\n",
        "if np.std(diff_value) >= 0.17: # Check whether std. of difference is over 0.17\n",
        "\tfor i in range(len(diff_value)):\n",
        "\t\tif diff_value[i] < 0.25: # Include model if its difference of validation sets is less than 0.25\n",
        "\t\t\tvari_valid_per.append(valid_close_per[i])\n",
        "\t\t\tvari_loc.append(i)\n",
        "\n",
        "else: \n",
        "\tvari_valid_per = valid_close_per # If std. of difference is less than 0.17, use close (or far) validation set\n",
        "\tvari_loc = [0,1,2,3]\n",
        "\n",
        "# Calculate the weight values using EER values from close (or far) validation set in remained models\n",
        "vari_weight = []\n",
        "for i in range(len(vari_valid_per)): \n",
        "\tvari_weight.append(round(np.divide((1/vari_valid_per[i]) ,(np.sum(np.divide(1,vari_valid_per)))), 3))\n",
        "\t\n",
        "print('Std. of difference between validation sets: ',np.std(diff_value))\n",
        "print('Weights in variation fusion: ',vari_weight)\n",
        "\n",
        "vari_time = time.time() - start_time_vari"
      ],
      "execution_count": 21,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Std. of difference between validation sets:  0.11582439344753585\n",
            "Weights in variation fusion:  [0.371, 0.031, 0.371, 0.226]\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "epu60GQAvaZl",
        "colab_type": "text"
      },
      "source": [
        "**Variation - Min Fusion**"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "iYM9J5iIvZ4n",
        "colab_type": "code",
        "outputId": "d3e4b1ea-d651-4677-a542-7f7ac7fc2039",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 34
        }
      },
      "source": [
        "start_time_min = time.time() # Time starts!    \n",
        "\n",
        "thres_matrix_fusion = np.zeros((len(test_target),len(thres)))\n",
        "total_matrix_fusion = np.zeros((len(thres),4)) #acc,far,frr,recall(order of column)\n",
        "\n",
        "vari_min_test_logit = test_all_logit[vari_loc[vari_valid_per.index(min(vari_valid_per))],:]\n",
        "\n",
        "for j in range(len(thres)):\n",
        "    for i in range(len(test_target)):\n",
        "        if vari_min_test_logit[i] < thres[j]:\n",
        "            thres_matrix_fusion[i,j] = 0\n",
        "        else:\n",
        "            thres_matrix_fusion[i,j] = 1\n",
        "\n",
        "for i in range(len(thres)):\n",
        "\n",
        "    equal_logit = np.equal(thres_matrix_fusion[:,i], test_target[:,1]) \n",
        "    unique2, True_pos_neg_test = np.unique(thres_matrix_fusion[np.where(equal_logit==True),i], return_counts=True)\n",
        "    unique3, False_pos_neg_test = np.unique(thres_matrix_fusion[np.where(equal_logit==False),i], return_counts=True) \n",
        "\n",
        "    if np.shape(True_pos_neg_test)==(1,):\n",
        "        if unique2[0] == 0:\n",
        "            True_pos_neg_test = [True_pos_neg_test[0], 0]\n",
        "        else:\n",
        "            True_pos_neg_test = [0, True_pos_neg_test[0]]\n",
        "\n",
        "    if np.shape(False_pos_neg_test)==(1,):\n",
        "        if unique3[0] == 0:\n",
        "            False_pos_neg_test = [False_pos_neg_test[0], 0]\n",
        "        else:\n",
        "            False_pos_neg_test = [0, False_pos_neg_test[0]]\n",
        "\n",
        "    if np.shape(True_pos_neg_test)==(0,):\n",
        "        True_pos_neg_test = [0, 0]\n",
        "\n",
        "    if np.shape(False_pos_neg_test)==(0,):\n",
        "        False_pos_neg_test = [0, 0]\n",
        "\n",
        "    Recall_test= True_pos_neg_test[1]/(False_pos_neg_test[0]+True_pos_neg_test[1])\n",
        "    Specific_test = True_pos_neg_test[0] /(True_pos_neg_test[0]+False_pos_neg_test[1])\n",
        "\n",
        "    ACC_test = (True_pos_neg_test[0]+True_pos_neg_test[1]) / (len(test_target))\n",
        "    FAR_test = 1 - Specific_test\n",
        "    FRR_test = 1 - Recall_test\n",
        "\n",
        "    total_matrix_fusion[i,:] = ACC_test, FAR_test, FRR_test, Recall_test\n",
        "\n",
        "min_time = time.time() - start_time_min\n",
        "\n",
        "elapsed = train_time + test_time + valid_time + vari_time + min_time\n",
        "\n",
        "print('Execution time for variation - min fusion: {:.2f} seconds'.format(elapsed))"
      ],
      "execution_count": 22,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Execution time for variation - min fusion: 364.93 seconds\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "dOAPodRYvaFB",
        "colab_type": "code",
        "outputId": "0a2f27d8-dae7-4f0d-b9be-298937716926",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 607
        }
      },
      "source": [
        "fig = plt.figure()\n",
        "ax = plt.subplot(111)\n",
        "ax.plot(thres, total_matrix_fusion[:,1], 'b', label='FAR')\n",
        "ax.plot(thres, total_matrix_fusion[:,2],'r', label='FRR')\n",
        "ax.legend()\n",
        "plt.title('FAR vs FRR (Test)')\n",
        "plt.xlabel('Threshold')\n",
        "plt.ylabel('Err Rate')\n",
        "plt.show()\n",
        "\n",
        "EER_line = thres[::-1]\n",
        "fig = plt.figure()\n",
        "ax2 = plt.subplot(111)\n",
        "ax2.plot(total_matrix_fusion[:,1], total_matrix_fusion[:,3],'r',label='ROC')\n",
        "ax2.plot(thres, EER_line, 'b',label='EER Line')\n",
        "ax2.legend()\n",
        "plt.title('ROC Curve (Test)')\n",
        "plt.xlabel('FAR')\n",
        "plt.ylabel('TPR')\n",
        "plt.show()\n",
        "\n",
        "EER_loc_test = np.argmin(abs(total_matrix_fusion[:,1] - total_matrix_fusion[:,2]))\n",
        "print('EER with variation - min fusion: {:.3f} '.format((total_matrix_fusion[EER_loc_test,1]+total_matrix_fusion[EER_loc_test,2])/2))\n",
        "print('Accuracy with variation - min fusion: {:.3f} '.format(total_matrix_fusion[EER_loc_test,0]))"
      ],
      "execution_count": 23,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "image/png": "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\n",
            "text/plain": [
              "<Figure size 432x288 with 1 Axes>"
            ]
          },
          "metadata": {
            "tags": [],
            "needs_background": "light"
          }
        },
        {
          "output_type": "display_data",
          "data": {
            "image/png": "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\n",
            "text/plain": [
              "<Figure size 432x288 with 1 Axes>"
            ]
          },
          "metadata": {
            "tags": [],
            "needs_background": "light"
          }
        },
        {
          "output_type": "stream",
          "text": [
            "EER with variation - min fusion: 0.000 \n",
            "Accuracy with variation - min fusion: 1.000 \n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "A2dte2uLZFIg",
        "colab_type": "text"
      },
      "source": [
        "**Variation - Sum Fusion**"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "7DTHrN13ZXGF",
        "colab_type": "code",
        "outputId": "ec34d98f-b55c-4a19-8c09-a3def185a31e",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 34
        }
      },
      "source": [
        "start_time_sum = time.time() # Time starts!    \n",
        "\n",
        "thres_matrix_fusion2 = np.zeros((len(test_target),len(thres)))\n",
        "total_matrix_fusion2 = np.zeros((len(thres),4)) #acc,far,frr,recall(order of column)\n",
        "\n",
        "vari_sum_test_logit = np.zeros(np.shape(test_all_logit[0,:]))\n",
        "\n",
        "for i in range(len(vari_loc)):\n",
        "\tvari_sum_test_logit += vari_weight[i] * test_all_logit[vari_loc[i],:]\n",
        "\n",
        "for j in range(len(thres)):\n",
        "    for i in range(len(test_target)):\n",
        "        if vari_sum_test_logit[i] < thres[j]:\n",
        "            thres_matrix_fusion2[i,j] = 0\n",
        "        else:\n",
        "            thres_matrix_fusion2[i,j] = 1\n",
        "\n",
        "for i in range(len(thres)):\n",
        "\n",
        "    equal_logit = np.equal(thres_matrix_fusion2[:,i], test_target[:,1]) \n",
        "    unique2, True_pos_neg_test = np.unique(thres_matrix_fusion2[np.where(equal_logit==True),i], return_counts=True)\n",
        "    unique3, False_pos_neg_test = np.unique(thres_matrix_fusion2[np.where(equal_logit==False),i], return_counts=True) \n",
        "\n",
        "    if np.shape(True_pos_neg_test)==(1,):\n",
        "        if unique2[0] == 0:\n",
        "            True_pos_neg_test = [True_pos_neg_test[0], 0]\n",
        "        else:\n",
        "            True_pos_neg_test = [0, True_pos_neg_test[0]]\n",
        "\n",
        "    if np.shape(False_pos_neg_test)==(1,):\n",
        "        if unique3[0] == 0:\n",
        "            False_pos_neg_test = [False_pos_neg_test[0], 0]\n",
        "        else:\n",
        "            False_pos_neg_test = [0, False_pos_neg_test[0]]\n",
        "\n",
        "    if np.shape(True_pos_neg_test)==(0,):\n",
        "        True_pos_neg_test = [0, 0]\n",
        "\n",
        "    if np.shape(False_pos_neg_test)==(0,):\n",
        "        False_pos_neg_test = [0, 0]\n",
        "\n",
        "    Recall_test= True_pos_neg_test[1]/(False_pos_neg_test[0]+True_pos_neg_test[1])\n",
        "    Specific_test = True_pos_neg_test[0] /(True_pos_neg_test[0]+False_pos_neg_test[1])\n",
        "\n",
        "    ACC_test = (True_pos_neg_test[0]+True_pos_neg_test[1]) / (len(test_target))\n",
        "    FAR_test = 1 - Specific_test\n",
        "    FRR_test = 1 - Recall_test\n",
        "\n",
        "    total_matrix_fusion2[i,:] = ACC_test, FAR_test, FRR_test, Recall_test\n",
        "\n",
        "sum_time = time.time() - start_time_sum  \n",
        "\n",
        "elapsed = train_time + test_time + valid_time + vari_time + sum_time\n",
        "\n",
        "print('Execution time for variation - sum fusion: {:.2f} seconds'.format(elapsed))\n"
      ],
      "execution_count": 24,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Execution time for variation - sum fusion: 363.57 seconds\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "BKg3H3fHuz3W",
        "colab_type": "code",
        "outputId": "f8f3e048-0401-49e6-d31b-300785050a80",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 607
        }
      },
      "source": [
        "fig = plt.figure()\n",
        "ax = plt.subplot(111)\n",
        "ax.plot(thres, total_matrix_fusion2[:,1], 'b', label='FAR')\n",
        "ax.plot(thres, total_matrix_fusion2[:,2],'r', label='FRR')\n",
        "ax.legend()\n",
        "plt.title('FAR vs FRR (Test)')\n",
        "plt.xlabel('Threshold')\n",
        "plt.ylabel('Err Rate')\n",
        "plt.show()\n",
        "\n",
        "EER_line = thres[::-1]\n",
        "fig = plt.figure()\n",
        "ax2 = plt.subplot(111)\n",
        "ax2.plot(total_matrix_fusion2[:,1], total_matrix_fusion2[:,3],'r',label='ROC')\n",
        "ax2.plot(thres, EER_line, 'b',label='EER Line')\n",
        "ax2.legend()\n",
        "plt.title('ROC Curve (Test)')\n",
        "plt.xlabel('FAR')\n",
        "plt.ylabel('TPR')\n",
        "plt.show()\n",
        "\n",
        "EER_loc_test = np.argmin(abs(total_matrix_fusion2[:,1] - total_matrix_fusion2[:,2]))\n",
        "print('EER with variation - sum fusion: {:.3f} '.format((total_matrix_fusion2[EER_loc_test,1]+total_matrix_fusion2[EER_loc_test,2])/2))\n",
        "print('Accuracy with variation - sum fusion: {:.3f} '.format(total_matrix_fusion2[EER_loc_test,0]))"
      ],
      "execution_count": 25,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "image/png": "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\n",
            "text/plain": [
              "<Figure size 432x288 with 1 Axes>"
            ]
          },
          "metadata": {
            "tags": [],
            "needs_background": "light"
          }
        },
        {
          "output_type": "display_data",
          "data": {
            "image/png": "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\n",
            "text/plain": [
              "<Figure size 432x288 with 1 Axes>"
            ]
          },
          "metadata": {
            "tags": [],
            "needs_background": "light"
          }
        },
        {
          "output_type": "stream",
          "text": [
            "EER with variation - sum fusion: 0.000 \n",
            "Accuracy with variation - sum fusion: 1.000 \n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "wU7MKc7vt-ST",
        "colab_type": "text"
      },
      "source": [
        "**Variation - Product Fusion**"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "QLeNmlcDt-jO",
        "colab_type": "code",
        "outputId": "29c6fd83-33ca-4bed-ba81-49a33b71de39",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 34
        }
      },
      "source": [
        "start_time_product = time.time() # Time starts!    \n",
        "\n",
        "thres_matrix_fusion3 = np.zeros((len(test_target),len(thres)))\n",
        "total_matrix_fusion3 = np.zeros((len(thres),4)) #acc,far,frr,recall(order of column)\n",
        "\n",
        "vari_product_test_logit = np.ones(np.shape(test_all_logit[0,:]))\n",
        "\n",
        "for i in range(len(vari_loc)):\n",
        "\tvari_product_test_logit = np.multiply(vari_product_test_logit, np.power(test_all_logit[vari_loc[i],:],vari_weight[i]))\n",
        "\n",
        "for j in range(len(thres)):\n",
        "    for i in range(len(test_target)):\n",
        "        if vari_product_test_logit[i] < thres[j]:\n",
        "            thres_matrix_fusion3[i,j] = 0\n",
        "        else:\n",
        "            thres_matrix_fusion3[i,j] = 1\n",
        "\n",
        "for i in range(len(thres)):\n",
        "\n",
        "    equal_logit = np.equal(thres_matrix_fusion3[:,i], test_target[:,1]) \n",
        "    unique2, True_pos_neg_test = np.unique(thres_matrix_fusion3[np.where(equal_logit==True),i], return_counts=True)\n",
        "    unique3, False_pos_neg_test = np.unique(thres_matrix_fusion3[np.where(equal_logit==False),i], return_counts=True) \n",
        "\n",
        "    if np.shape(True_pos_neg_test)==(1,):\n",
        "        if unique2[0] == 0:\n",
        "            True_pos_neg_test = [True_pos_neg_test[0], 0]\n",
        "        else:\n",
        "            True_pos_neg_test = [0, True_pos_neg_test[0]]\n",
        "\n",
        "    if np.shape(False_pos_neg_test)==(1,):\n",
        "        if unique3[0] == 0:\n",
        "            False_pos_neg_test = [False_pos_neg_test[0], 0]\n",
        "        else:\n",
        "            False_pos_neg_test = [0, False_pos_neg_test[0]]\n",
        "\n",
        "    if np.shape(True_pos_neg_test)==(0,):\n",
        "        True_pos_neg_test = [0, 0]\n",
        "\n",
        "    if np.shape(False_pos_neg_test)==(0,):\n",
        "        False_pos_neg_test = [0, 0]\n",
        "\n",
        "    Recall_test= True_pos_neg_test[1]/(False_pos_neg_test[0]+True_pos_neg_test[1])\n",
        "    Specific_test = True_pos_neg_test[0] /(True_pos_neg_test[0]+False_pos_neg_test[1])\n",
        "\n",
        "    ACC_test = (True_pos_neg_test[0]+True_pos_neg_test[1]) / (len(test_target))\n",
        "    FAR_test = 1 - Specific_test\n",
        "    FRR_test = 1 - Recall_test\n",
        "\n",
        "    total_matrix_fusion3[i,:] = ACC_test, FAR_test, FRR_test, Recall_test\n",
        "\n",
        "product_time = time.time() - start_time_product  \n",
        "\n",
        "elapsed = train_time + test_time + valid_time + vari_time + product_time\n",
        "\n",
        "print('Execution time for variation - product fusion: {:.2f} seconds'.format(elapsed))"
      ],
      "execution_count": 26,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Execution time for variation - product fusion: 363.57 seconds\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "yJAIciu_vWlj",
        "colab_type": "code",
        "outputId": "181fa355-3fee-41c8-a63e-e297861962b1",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 607
        }
      },
      "source": [
        "fig = plt.figure()\n",
        "ax = plt.subplot(111)\n",
        "ax.plot(thres, total_matrix_fusion3[:,1], 'b', label='FAR')\n",
        "ax.plot(thres, total_matrix_fusion3[:,2],'r', label='FRR')\n",
        "ax.legend()\n",
        "plt.title('FAR vs FRR (Test)')\n",
        "plt.xlabel('Threshold')\n",
        "plt.ylabel('Err Rate')\n",
        "plt.show()\n",
        "\n",
        "EER_line = thres[::-1]\n",
        "fig = plt.figure()\n",
        "ax2 = plt.subplot(111)\n",
        "ax2.plot(total_matrix_fusion3[:,1], total_matrix_fusion3[:,3],'r',label='ROC')\n",
        "ax2.plot(thres, EER_line, 'b',label='EER Line')\n",
        "ax2.legend()\n",
        "plt.title('ROC Curve (Test)')\n",
        "plt.xlabel('FAR')\n",
        "plt.ylabel('TPR')\n",
        "plt.show()\n",
        "\n",
        "EER_loc_test = np.argmin(abs(total_matrix_fusion3[:,1] - total_matrix_fusion3[:,2]))\n",
        "print('EER with variation - product fusion: {:.3f} '.format((total_matrix_fusion3[EER_loc_test,1]+total_matrix_fusion3[EER_loc_test,2])/2))\n",
        "print('Accuracy with variation - product fusion: {:.3f} '.format(total_matrix_fusion3[EER_loc_test,0]))"
      ],
      "execution_count": 27,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "image/png": "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tnfddRcjRoxg0qRJHH300Xzuc5+rU9TWr2zenL2BLVtWyu6T+PhoRMyLiOaIaB49ustvSL+KfRUUlFmVhg0bxvLly3c/Oi6RsmDBAt75znfyk5/8ZI/tJ0+ezPLly3nwwQe54447uOeee+oQtfUrHW9cJbUE65kI1gHjKubH5stK4Zaq9Sd/+tOf2LJlC5dddhkLFizocpthw4YxadIk1q0r7dfCUlFyIqjnx0cXAXMkLQTeBWyKiCfrGI8NcBdeCMuXF7vPSZPgqqt63mbr1q1MmjQJgKamJm677TYWLlzIzJkzmTx5MqtWreKpp57i8MMP3+N1Gzdu5JFHHuHkk08uNmhLT6qJQNIC4BRglKRW4CvAYICIuB5YDJwBrAZeBD5aViyV3BqyWutoDVVasGABt912G/vttx9nnXUWt956K3PmzAHg7rvv5rjjjuORRx7hwgsv5LWvfW09wrb+JNVEEBHn9bI+gL8ua/zO3Bqy3v5yr5Xf//73PPLII5x66qkAbN++naampt2JYPLkydxxxx08+uijnHjiiZx77rm7KwprUAP4HEFduCKweluwYAGXXHIJa9euZe3ataxfv57169fz2GN7XiW4qamJuXPncvnll9cpUus3nAiK4YrA+ouFCxcyffr0PZZNnz6dhQsX7rXtxz/+cZYuXbr7I6fWoFJtDZlZZsuWLXvMr1mzZq9trrjiit3Tp5xyyu7pYcOG+VND5oqgaG4NmVlynAiK4daQmSXLiaBYrgjMLDlOBMVwRWBmyXIiKJYrAjNLjhNBMVwRmFmynAjM0tbVZah7utz0/PnzGT169O51V155ZR2jt37B3yMolltDVmtdXWto7dq1uy8lsXXrVo4//nimT5/OSSedBMCMGTO49tpraW9v581vfjNnn30248aN62r31ghcERTDrSHrr3q63PTIkSM56qijePJJX5i3obkiKJYrggZWp+tQd3UZ6ko9XW768ccfZ9u2bRx77LHFxWzpcSIohisCq5euWkPQ8+Wmb775ZpYuXcof//hHrr32WoYOHVrLkK2/cSIwK0h/uQ51rqfLTXecI2hpaeG0005j6tSpvi9BI/M5gmK5NWT9TU+Xm25ububDH/4wV199dR0is37DiaAYbg1Zf9bT5aa/8IUv8P3vf5/NmzfXPjDrH9waKpYrAqu1zpehhuxS091dbnrW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\n",
            "text/plain": [
              "<Figure size 432x288 with 1 Axes>"
            ]
          },
          "metadata": {
            "tags": [],
            "needs_background": "light"
          }
        },
        {
          "output_type": "display_data",
          "data": {
            "image/png": "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\n",
            "text/plain": [
              "<Figure size 432x288 with 1 Axes>"
            ]
          },
          "metadata": {
            "tags": [],
            "needs_background": "light"
          }
        },
        {
          "output_type": "stream",
          "text": [
            "EER with variation - product fusion: 0.000 \n",
            "Accuracy with variation - product fusion: 1.000 \n"
          ],
          "name": "stdout"
        }
      ]
    }
  ]
}